// const { compileFunction } = require("node:vm")

class Vue {
  constructor(options) {
    this.$data = options.data
    // 调用数据劫持方法
    Observe(this.$data)

    // 属性代理,直接通过vm就能获取到属性
    Object.keys(this.$data).forEach((key) => {
      Object.defineProperty(this, key, {
        enumerable: true,
        configurable: true,
        get() {
          return this.$data[key]
        },
        set(newValue) {
          this.$data[key] = newValue
        },
      })
    })
    Compile(options.el, this)
  }
}

// 数据劫持方法
function Observe(obj) {
  if (!obj || typeof obj !== 'object') return
  const dep = new Dep()
  Object.keys(obj).forEach((key) => {
    let value = obj[key]
    // 递归调用，为每个对象中的对象设置set和get
    Observe(value)
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get() {
        // console.log('get方法调用')
        // 只要执行了下面这一行，那么刚才 new 的 Watcher 实例，
        // 就被放到了 dep.subs 这个数组中了
        Dep.target && dep.addSub(Dep.target)
        return value
      },
      set(newValue) {
        console.log('set方法调用')
        value = newValue
        Observe(value)

        // 执行更新方法
        dep.notify()
      },
    })
  })
}

// 编译
function Compile(el, vm) {
  vm.$el = document.querySelector(el)
  const fragment = document.createDocumentFragment()
  while ((childNode = vm.$el.firstChild)) {
    fragment.appendChild(childNode)
  }
  replace(fragment)

  function replace(node) {
    const Regx = /\{\{\s*(\S+)\s*\}\}/
    if (node.nodeType === 3) {
      const text = node.textContent
      const regxResult = Regx.exec(text)
      if (regxResult) {
        const value = regxResult[1].split('.').reduce((newObj, k) => newObj[k], vm)
        node.textContent = text.replace(Regx, value)
        // node结点知道怎么更新自己了
        // 把更新自己的代码传给Watch，在watch中添加到dep数组中
        new Watch(vm, regxResult[1], (newValue) => {
          node.textContent = text.replace(Regx, newValue)
        })
      }
      return
    }

    if (node.nodeType === 1 && node.tagName.toUpperCase() === "INPUT") {
      const attrs = Array.from(node.attributes)
      const findResult = attrs.find((x) => x.name === 'v-model')
      if (findResult) {
        const expStr = findResult.value
        const value = expStr.split('.').reduce((newObj, k) => newObj[k], vm)
        node.value = value

        // 创建watch 添加更新新值的回调
        new Watch(vm, expStr, (newValue) => {
          node.value = newValue
        })


        // 监听input输入框
        node.addEventListener('input', (e) => {
          const keyArr = expStr.split('.')
          const obj = keyArr.slice(0, keyArr.length - 1).reduce((newObj, k) => newObj[k], vm)
          const leafKey = keyArr[keyArr.length - 1]
          obj[leafKey] = e.target.value
        })
      }
    }

    // 递归寻找text结点
    node.childNodes.forEach((child) => replace(child))
  }

  vm.$el.appendChild(fragment)
}

class Dep {
  constructor() {
    this.subs = []
  }
  addSub(watcher) {
    this.subs.push(watcher)
  }
  notify() {
    this.subs.forEach((item) => {
      item.update()
    })
  }
}

class Watch {
  constructor(vm, key, cb) {
    this.vm = vm
    this.key = key
    this.cb = cb

    Dep.target = this
    key.split('.').reduce((newObj, k) => newObj[k], vm)
    Dep.target = null
  }
  update() {
    // 获取新值
    const value = this.key.split('.').reduce((newObj, k) => newObj[k], this.vm)
    // 执行回调函数，更新新值
    this.cb(value)
  }
}